/*
    Dominate 3D - A cube-based game of life

    Copyright (C) 2007, Craig H. Miller (kidmosey@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "dominate3d.h"
#include "game.h"

#include "player.h"
#include "cell.h"
#include "glUtil.h"
#include "client.h"

#include "resource/resource.h"

extern SharedObject<Cube> g_Cube;
extern SharedObject<Client> g_Client;

float g_ChatStartTime = 0.0f;
std::vector<std::pair<std::string, Color> > g_listChatStrings;

namespace Game
{
    SharedObject<bool> m_bFinished;
    int m_newDialog = 0;

    void SetupGL();
    void InitCamera();

    void RenderChatText(const GLFont &font);
}

void RenderScene()
{
    g_Cube.Lock().Render();
    g_Cube.Unlock();
}

INT_PTR CALLBACK InGameDlgProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            SetFocus(hwndDlg);
            return 1;
        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_EXITTOMAIN:
                    EndDialog(hwndDlg, IDD_MAIN);
                    return TRUE;

                case IDC_EXITGAME:
                    EndDialog(hwndDlg, 0);
                    return TRUE;

                case IDOK:
                    EndDialog(hwndDlg, 1);
                    return TRUE;
            }
        return 0;
    }

    return 0;
}

namespace GameInput
{
    void MouseButtonHandler(int button, int action)
    {
        if (button == GLFW_MOUSE_BUTTON_RIGHT)
        {
            // reset to default
            g_Cube.Lock().m_vRotVelocity = CVector3D();
            g_Cube.Unlock();
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT)
        {
            if (action == GLFW_RELEASE)
            {
                int x, y;
                glfwGetMousePos(&x, &y);

                // Setup GL flags
                Game::SetupGL();

                // Initialize the scene
                Game::InitCamera();

                Cell *cell = (Cell *)glGetSelection(x,y,RenderScene);

                SocketParam params[] = {
                    itoa((cell)?cell->GetID():0)
                };
                ::g_Client.Lock().SendMessage(SV_PICKCELL, std::vector<SocketParam>(params, params + COUNT(params)));
                ::g_Client.Unlock();
            }
        }
    }

    std::string textStr;

    void GLFWCALL CharHandler(int key, int action)
    {
        if (action == GLFW_PRESS && isprint(key))
        {
            textStr += (char )key;
            g_ChatStartTime = glfwGetTime();
        }
    }

    void GLFWCALL KeyHandler(int key, int action)
    {
        switch (key)
        {
            case GLFW_KEY_ENTER:
                if (action == GLFW_PRESS)
                {
                    if (textStr.length())
                    {
                        //g_listChatStrings.push_back(std::pair<std::string, Color>(textStr, Color()));
                        SocketParam params[] = {
                            textStr
                        };
                        ::g_Client.Lock().SendMessage(CS_CHATTEXT, std::vector<SocketParam>(params, params + COUNT(params)));
                        ::g_Client.Unlock();
                    }

                    g_ChatStartTime = glfwGetTime();
                    textStr = "";
                }
                break;

            case GLFW_KEY_ESC:
                // automatically kick to main menu if game is over.
                if (::g_Cube.Lock().IsFull())
                {
                    Game::m_newDialog = IDD_MAIN;
                }
                else
                {
                    Game::m_newDialog = DialogBox(
                        GetModuleHandle(NULL),
                        MAKEINTRESOURCE(IDD_INGAME),
                        GetActiveWindow(),
                        InGameDlgProc);
                }
                ::g_Cube.Unlock();

                if (Game::m_newDialog != 1)
                {
                    Game::m_bFinished.Lock() = true;
                    Game::m_bFinished.Unlock();
                }
                break;
        }
    }

    // Handle mouse motion - updates the Mouse structure
    void MousePosHandler(int x, int y)
    {
        static int prev_x = 0, prev_y = 0;
        if (!prev_x)
        {
            prev_x = x;
            prev_y = y;
        }
        int dx = x - prev_x;
        int dy = y - prev_y;

        if (glfwGetMouseButton(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
        {
            g_Cube.Lock().m_vRotVelocity += CVector3D(-dy, dx, 0);
            g_Cube.Unlock();
        }

        prev_x = x;
        prev_y = y;
    }

    void InitInputHandlers()
    {
        glfwSetCharCallback(GameInput::CharHandler);
        glfwSetKeyCallback(GameInput::KeyHandler);
        glfwSetMouseButtonCallback(GameInput::MouseButtonHandler);
        glfwSetMousePosCallback(GameInput::MousePosHandler);
    }
}

namespace Game
{
    void InitWindow()
    {
        glfwInit();

        if ( !glfwOpenWindow( 800, 800, 0, 0, 0, 0, 32, 0, GLFW_WINDOW ) )
        {
            glfwTerminate();
            return;
        }

        glfwSetWindowTitle("Dominate 3D");
        glfwSwapInterval(0);
    }

    bool Finished()
    {
        bool bFinished = m_bFinished.Lock();
        m_bFinished.Unlock();

        return bFinished || !glfwGetWindowParam(GLFW_OPENED);
    }

    void SetupGL()
    {
        glHint(GL_FOG_HINT, GL_NICEST);
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
		glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);


        glShadeModel(GL_SMOOTH);
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_POLYGON_SMOOTH);
        glEnable(GL_POINT_SMOOTH);

        glEnable(GL_NORMALIZE);
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_AUTO_NORMAL);

        glDisable(GL_CULL_FACE);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);

        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);

        glLineWidth(1);
    }

    void InitScreen()
    {
        int width, height;
        glfwGetWindowSize( &width, &height );
        height = height > 0 ? height : 1;

        glViewport( 0, 0, width, height );

        glClearColor( 0.4f, 0.4f, 0.6f, 1.0f );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    }

    void InitCamera()
    {
        int width, height;
        glfwGetWindowSize( &width, &height );

        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 65.0f, (GLfloat)width/(GLfloat)height, 1.0f, 100.0f );

        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();

        glInitNames();												// Initializes The Name Stack
        glPushName(0);												// Push 0 (At Least One Entry) Onto The Stack

        gluLookAt(0.0f, 0.0f, -2.0f,
                  0.0f, 0.0f, 0.0f,
                  0.0f, 1.0f, 0.0f );
    }

    void RenderHUD(const GLFont &font);
    void RenderPlayerScore(int x, int y, Player *player, const GLFont &font);

    void Terminate()
    {
        glfwCloseWindow();
        glfwTerminate();
    }

class Mirror
{
public:
    Mirror()
    {
        m_matrix.LookAt(CVector3D(1.0,1.25,-3.25), CVector3D(0,0,0));
        m_reflectionMatrix = m_matrix;
        m_reflectionMatrix.FastInverse();

        SetLight();
    }

    Mirror(const CVector3D quad[4])
    {}

    Mirror(const CVector3D &normal, float width, float height)
    {}

    ~Mirror()
    {}

    void DrawMirror()
    {
        float w = 1.0, h = 0.75;

        glBegin(GL_QUADS);
        glVertex3f(-w,h,0);
        glVertex3f( w,h,0);
        glVertex3f( w,-h,0);
        glVertex3f(-w,-h,0);
        glEnd();
    }

    void CreateStencil()
    {
        glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        glDisable(GL_DEPTH_TEST);

        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilFunc(GL_ALWAYS, 1, 0xffffffff);

        DrawMirror();

        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glPopAttrib();
    }

    void RenderReflection()
    {
        glStencilFunc(GL_EQUAL, 1, 0xffffffff);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

        // Apply the reflection matrix
        glPushMatrix();
        glMultMatrixf((GLfloat *)&m_reflectionMatrix);
        glScalef(-1,1,-1);

        glCullFace(GL_FRONT);
        g_Cube.Lock().Render();
        g_Cube.Unlock();
        glCullFace(GL_BACK);

        glPopMatrix();
    }

    void RenderMirror()
    {
        glEnable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        // draw the front face
        glColor4f(1,1,1,0.49);
        DrawMirror();
        glDisable(GL_BLEND);

        // draw the back face
        glFrontFace(GL_CW);
        glColor3f(0.5,0.5,0.5);
        DrawMirror();
        glFrontFace(GL_CCW);

        glEnable(GL_DEPTH_TEST);
    }

    CMatrix m_matrix, m_reflectionMatrix;

    //CLight light;
    void SetLight()
    {
/*
        light.ambient = CFloat4(0.75,0.75,0.75,1);
        light.diffuse = CFloat4(1,1,1,1);
        light.specular = CFloat4(1,1,1,1);
        light.position = CFloat4(0,0,-1);
        light.direction = CFloat4(0,0,1);
*/
    }

    void Render()
    {
        glEnable(GL_CULL_FACE);
        glEnable(GL_COLOR_MATERIAL);

        glPushMatrix();
        glLoadIdentity();

        // Normalize our location matrix.
        glMultMatrixf((GLfloat *)&m_matrix);
        glScalef(1,1,-1);

        //glEnable(GL_LIGHTING);
        //light.Enable();

        glEnable(GL_STENCIL_TEST);
        CreateStencil();
        RenderReflection();
        glDisable(GL_STENCIL_TEST);

        RenderMirror();

        glPopMatrix();
    }
};

    int CreateTexture(const char fname[])
    {
        int texID = 0;

        char *imgFname = CopyBinaryFile(fname);

        glfwLoadTexture2D(imgFname, GLFW_BUILD_MIPMAPS_BIT);
        glGetIntegerv(GL_TEXTURE_BINDING_2D, &texID);

        if (imgFname) unlink(imgFname);

        return texID;
    }

    int DoLoop()
    {
        // Initialize the main game window
        Game::InitWindow();

        // Initialize our key/mouse handlers
        GameInput::InitInputHandlers();

        // load our texture
        int texID = CreateTexture(MAKEINTRESOURCE(IDR_TEX1));

        // Initialize our font
        GLFont font(MAKEINTRESOURCE(IDR_FONT));

        Mirror mirror;

        // while the game is not finished
        m_bFinished.Reset(new bool(false));
        while (!Game::Finished())
        {
            // Setup GL flags
            SetupGL();

            // Initialize the scene
            InitScreen();
            InitCamera();

            // Update the cube
            g_Cube.Lock().Update();
            g_Cube.Unlock();

            mirror.Render();

            // Render the cube
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texID);
            g_Cube.Lock().Render();
            g_Cube.Unlock();

            // Render the hud
            Game::RenderHUD(font);

            glfwSwapBuffers();
            Sleep(1); // avoid locking resources
        }

        // Cleanup
        Game::Terminate();

        return Game::m_newDialog;
    }
}

#if 0
extern Game *g_currentGame;
static VOID CALLBACK TimerProc(
    HWND hwnd,
    UINT uMsg,
    UINT_PTR idEvent,
    DWORD dwTime
)
{
    g_currentGame->m_currentTime--;
    if (g_currentGame->m_currentTime < 0)
        g_currentGame->m_currentTime = 0;
}

Color Game::s_playerColors[] =
{
/* Red  	 */  Color((long )0xFF0000FF),
//* AliceBlue  	 */  Color((long )0xF0F8FFFF),
//* Aqua  	 */  Color((long )0x00FFFFFF),
//* Aquamarine  	 */  Color((long )0x7FFFD4FF),
/* Blue  	 */  Color((long )0x0000FFFF),
//* BlueViolet  	 */  Color((long )0x8A2BE2FF),
//* Brown  	 */  Color((long )0xA52A2AFF),
//* CadetBlue  	 */  Color((long )0x5F9EA0FF),
//* Chartreuse  	 */  Color((long )0x7FFF00FF),
//* Crimson  	 */  Color((long )0xDC143CFF),
//* Cyan  	 */  Color((long )0x00FFFFFF),
/* DarkCyan  	 */  Color((long )0x008B8BFF),
//* DarkGoldenRod  	 */  Color((long )0xB8860BFF),
//* DarkMagenta  	 */  Color((long )0x8B008BFF),
/* Darkorange  	 */  Color((long )0xFF8C00FF),
//* DarkOrchid  	 */  Color((long )0x9932CCFF),
/* DarkRed  	 */  Color((long )0x8B0000FF),
//* DarkSalmon  	 */  Color((long )0xE9967AFF),
//* DarkTurquoise  	 */  Color((long )0x00CED1FF),
/* DarkViolet  	 */  Color((long )0x9400D3FF),
//* DeepPink  	 */  Color((long )0xFF1493FF),
//* DeepSkyBlue  	 */  Color((long )0x00BFFFFF),
//* Fuchsia  	 */  Color((long )0xFF00FFFF),
/* Gold  	 */  Color((long )0xFFD700FF),
//* GoldenRod  	 */  Color((long )0xDAA520FF),
//* HoneyDew  	 */  Color((long )0xF0FFF0FF),
//* HotPink  	 */  Color((long )0xFF69B4FF),
//* IndianRed   	 */  Color((long )0xCD5C5CFF),
//* Indigo   	 */  Color((long )0x4B0082FF),
//* Lavender  	 */  Color((long )0xE6E6FAFF),
//* LavenderBlush  	 */  Color((long )0xFFF0F5FF),
/* LightBlue  	 */  Color((long )0xADD8E6FF),
//* LightCyan  	 */  Color((long )0xE0FFFFFF),
/* LightGoldenRodYellow  	 */  Color((long )0xFAFAD2FF),
/* LightPink  	 */  Color((long )0xFFB6C1FF),
//* LightSalmon  	 */  Color((long )0xFFA07AFF),
//* LightSkyBlue  	 */  Color((long )0x87CEFAFF),
//* LightSlateGrey  	 */  Color((long )0x778899FF),
//* LightSteelBlue  	 */  Color((long )0xB0C4DEFF),
/* LightYellow  	 */  Color((long )0xFFFFE0FF),
/* Magenta  	 */  Color((long )0xFF00FFFF),
/* Maroon  	 */  Color((long )0x800000FF),
/* MediumAquaMarine  	 */  Color((long )0x66CDAAFF),
/* MediumBlue  	 */  Color((long )0x0000CDFF),
/* MediumOrchid  	 */  Color((long )0xBA55D3FF),
/* MediumPurple  	 */  Color((long )0x9370D8FF),
/* MediumSlateBlue  	 */  Color((long )0x7B68EEFF),
/* MediumTurquoise  	 */  Color((long )0x48D1CCFF),
/* MediumVioletRed  	 */  Color((long )0xC71585FF),
/* MistyRose  	 */  Color((long )0xFFE4E1FF),
/* Navy  	 */  Color((long )0x000080FF),
/* Olive  	 */  Color((long )0x808000FF),
/* OliveDrab  	 */  Color((long )0x6B8E23FF),
/* Orange  	 */  Color((long )0xFFA500FF),
/* Orchid  	 */  Color((long )0xDA70D6FF),
/* PaleGoldenRod  	 */  Color((long )0xEEE8AAFF),
/* PaleTurquoise  	 */  Color((long )0xAFEEEEFF),
/* PaleVioletRed  	 */  Color((long )0xD87093FF),
/* PeachPuff  	 */  Color((long )0xFFDAB9FF),
/* Pink  	 */  Color((long )0xFFC0CBFF),
/* Plum  	 */  Color((long )0xDDA0DDFF),
/* PowderBlue  	 */  Color((long )0xB0E0E6FF),
/* Purple  	 */  Color((long )0x800080FF),
/* Red  	 */  Color((long )0xFF0000FF),
/* Salmon  	 */  Color((long )0xFA8072FF),
/* Sienna  	 */  Color((long )0xA0522DFF),
/* SkyBlue  	 */  Color((long )0x87CEEBFF),
/* SlateBlue  	 */  Color((long )0x6A5ACDFF),
/* SlateGrey  	 */  Color((long )0x708090FF),
/* SteelBlue  	 */  Color((long )0x4682B4FF),
/* Teal  	 */  Color((long )0x008080FF),
/* Thistle  	 */  Color((long )0xD8BFD8FF),
/* Tomato  	 */  Color((long )0xFF6347FF),
/* Turquoise  	 */  Color((long )0x40E0D0FF),
/* Violet  	 */  Color((long )0xEE82EEFF),
/* Wheat  	 */  Color((long )0xF5DEB3FF),
/* Yellow  	 */  Color((long )0xFFFF00FF)
};

/*
Color Game::s_playerColors[] =
{
    Color(0.0f,0.0f,1.0f),
    Color(0.5f,0.5f,1.0f),
    Color(0.0f,1.0f,1.0f),
    Color(1.0f,0.0f,0.0f),
    Color(1.0f,0.5f,0.5f),
    Color(1.0f,0.0f,1.0f),
    Color(1.0f,1.0f,0.0f),
    Color(0.5f,0.0f,0.5f)
};
*/

Game::Game(int num_players, int number_of_subdivisions)
        : m_bFinished(false), m_bGameOver(false)
{
    m_bRotating = false;
    m_currentSelection = 0;

    // Initialize our players
    for (int i = 0; i < num_players; i++)
    {
        m_listPlayers.push_back(new Player(s_playerColors[i]));
    }

    m_currentPlayer = m_listPlayers.begin();

    for (int i = 0; i < number_of_subdivisions; i++)
    {
        m_cube.Subdivide();
    }

    m_cube.AddPlayers(m_listPlayers);

    hWnd = GetActiveWindow();

    m_currentTime = atoi(GetOption("Score_TimeLimit").c_str());
    TimerHandle = SetTimer(
                      hWnd, // HWND hWnd,
                      0, // UINT_PTR nIDEvent,
                      1000, // UINT uElapse,
                      (TIMERPROC )TimerProc //TIMERPROC lpTimerFunc
                  );
}

Game::~Game()
{
    KillTimer(hWnd, TimerHandle);
    TimerHandle = 0;

    while (!m_listPlayers.empty())
    {
        delete m_listPlayers[0];
        m_listPlayers.erase(m_listPlayers.begin());
    }
}

Player *Game::GetLoser(Player *not_me) const
{
    int minIdx = 0;
    int minScore = m_listPlayers[minIdx]->GetScore();

    if (m_listPlayers[minIdx] == not_me)
    {
        minIdx++;
        minScore = m_listPlayers[minIdx]->GetScore();
    }

    for (size_t i = minIdx; i < m_listPlayers.size(); i++)
    {
        if (m_listPlayers[i] == not_me) continue;

        int score = m_listPlayers[i]->GetScore();
        if (score < minScore)
        {
            minIdx   = i;
            minScore = score;
        }
    }

    return m_listPlayers[minIdx];
}

Player *Game::GetWinner(Player *not_me) const
{
    int maxIdx = 0;
    int maxScore = m_listPlayers[maxIdx]->GetScore();

    if (m_listPlayers[maxIdx] == not_me)
    {
        maxIdx++;
        maxScore = m_listPlayers[maxIdx]->GetScore();
    }

    for (size_t i = 1; i < m_listPlayers.size(); i++)
    {
        if (m_listPlayers[i] == not_me) continue;

        int score = m_listPlayers[i]->GetScore();
        if (score > maxScore)
        {
            maxIdx   = i;
            maxScore = score;
        }
    }

    return m_listPlayers[maxIdx];
}

void Game::Abort()
{
    m_bGameOver = true;
    m_bFinished = true;
}

bool Game::IsFinished() const
{
    return m_bFinished;
}

void Game::Render(float zoom)
{
    glInitNames();												// Initializes The Name Stack
    glPushName(0);												// Push 0 (At Least One Entry) Onto The Stack

    glLoadIdentity();
    gluLookAt(0.0f, 0.0f, -10.0f,
              0.0f, 0.0f, 0.0f,
              0.0f, 1.0f, 0.0f );

    glScalef(zoom,zoom,zoom);

    m_cube.Render();

    if (m_currentSelection)
    {
        m_currentSelection->RenderHighlight();
    }
}

Player *Game::GetCurrentPlayer()
{
    if (m_currentPlayer != m_listPlayers.end())
        return *m_currentPlayer;

    return &Player::nullPlayer;
}

void Game::IncrementPlayer()
{
    if (GameOver()) return;

    std::vector<Player *>::iterator firstPlayer = m_currentPlayer;

    // go through the list of players once.
    for (size_t i = 0; i < m_listPlayers.size(); i++)
    {
        m_currentPlayer++;

        if (m_currentPlayer == m_listPlayers.end())
            m_currentPlayer = m_listPlayers.begin();

        if (m_cube.CanMove(*m_currentPlayer))
            break;
    }

    // game over
    if (m_currentPlayer == firstPlayer)
    {
        m_currentPlayer = m_listPlayers.end();

        EndGame();
    }

    if (OptionIsEnabled("Score_UseTimer"))
    {
        m_currentTime = atoi(GetOption("Score_TimeLimit").c_str());
    }
}

void Game::EndGame()
{

    // find the winner...
    Player *winner = m_listPlayers[0];
    for (size_t i = 1; i < m_listPlayers.size(); i++)
    {
        if (winner->GetScore() < m_listPlayers[i]->GetScore())
            winner = m_listPlayers[i];
    }

    // fill in the remaining cells
    std::vector<Cell *>::iterator i = m_cube.m_listCells.begin();
    while (i != m_cube.m_listCells.end())
    {
        assert(*i);

        if ((*i)->IsEmpty())
            (*i)->SetPlayer(winner);

        *i++;
    }

    m_bGameOver = true;
}

void Game::RenderHUD(const GLFont &font)
{
    glEnable2D();

    int w, h;
    glfwGetWindowSize(&w, &h);

    int x = 10, y = h - 42;
    std::vector<Player *>::iterator i = m_listPlayers.begin();
    while (i != m_listPlayers.end())
    {
        RenderPlayerScore(x, y, *i, font);

        y -= 32 + 5;
        *i++;
    }

    // Show game over screen
    if (g_currentGame->GameOver())
    {
        glColor3f(0,0,0);

        char *gameOver = "GAME OVER";
        glPushMatrix();
        glScalef(3,1,1);
        int posX = (w - strlen(gameOver)*16*3) / 6;
        font.Draw(posX, h/2, gameOver);
        glPopMatrix();

        gameOver = "Press ESC to return to main menu";
        posX = (w - strlen(gameOver)*16) / 2;
        font.Draw(posX, h/2-24, gameOver);
    }
    else if (OptionIsEnabled("Score_UseTimer"))
    {
        glColor3f(1,1,1);
        char tmp[8];
        itoa(m_currentTime, tmp, 10);
        font.Draw(w-(16*3), h-42, tmp);
    }

    glDisable2D();
}

void Game::RenderPlayerScore(int x, int y, Player *player, const GLFont &font)
{
    assert(player);

    int w, h;
    glfwGetWindowSize(&w, &h);

    player->Apply();

    glBegin(GL_QUADS);
    glVertex2d(x,y);
    glVertex2d(x+64,y);
    glVertex2d(x+64,y+32);
    glVertex2d(x,y+32);
    glEnd();

    glColor3f(0,0,0);

    //player->ApplyInverse();
    char tmp[32];
    itoa(player->GetScore(), tmp, 10);
    font.Draw(x+3, y + 4, tmp);
    font.Draw(x+64+4, y, player->GetName().c_str());

    glColor3f(0,0,0);
    glLineWidth(2);

    if (player == *m_currentPlayer)
        glColor3f(1,1,1);

    glBegin(GL_LINE_STRIP);
    glVertex2d(x,y);
    glVertex2d(x+64,y);
    glVertex2d(x+64,y+32);
    glVertex2d(x,y+32);
    glVertex2d(x,y);
    glEnd();
}
#endif

void Game::RenderHUD(const GLFont &font)
{
    glEnable2D();

    int w, h;
    glfwGetWindowSize(&w, &h);

    glPushMatrix();
    float scaleX = (float )w / (42.0f * 24.0f),
          scaleY = (float )h / (42.0f * 24.0f);
    glScalef(scaleX, scaleY, 1.0f);

    int x = 10, y = (int )((float )h/scaleY) - 42;
    std::vector<Player *> &listPlayers = g_Cube.Lock().m_listPlayers;
    std::vector<Player *>::iterator i = listPlayers.begin();
    while (i != listPlayers.end())
    {
        RenderPlayerScore(x, y, *i, font);

        y -= 32 + 5;
        *i++;
    }
    glPopMatrix();

    // Show game over screen when cube is full
    if (g_Cube.Lock().IsFull())
    {
        glColor3f(0,0,0);

        char *gameOver = "GAME OVER";
        glPushMatrix();
        glScalef(3,1,1);
        int posX = (w - strlen(gameOver)*16*3) / 6;
        font.Draw(posX, h/2, gameOver);
        glPopMatrix();

        gameOver = "Press ESC to return to main menu";
        posX = (w - strlen(gameOver)*16) / 2;
        font.Draw(posX, h/2-24, gameOver);
    }
    // Display the timer
    else if (OptionIsEnabled("Score_UseTimer"))
    {
        glColor3f(1,1,1);

        float timeLimit   = (float)atoi(GetOption("Score_TimeLimit").c_str());
        float timeElapsed = g_Cube.Lock().GetTimeElapsed();
        float currTime    = 0.0f;
        g_Cube.Unlock();

        if (timeElapsed < timeLimit)
            currTime = timeLimit - timeElapsed;

        font.Draw(w-(16*3), h-42, itoa((int)currTime).c_str());
    }
    g_Cube.Unlock();

    g_Cube.Unlock();

    RenderChatText(font);

    glDisable2D();
}

void Game::RenderChatText(const GLFont &font)
{
    int w, h;
    glfwGetWindowSize(&w, &h);

    // TODO: export FadeTime
    float FadeTime = 5.0f;
    float dur = glfwGetTime() - g_ChatStartTime;
    if (dur > FadeTime) dur = FadeTime;
    float fade_coeff = (FadeTime - dur) / FadeTime;

    float scale = 0.75f;
    glPushMatrix();
    glScalef(scale,scale,1);

    // render the text console
    if (fade_coeff > 0.0f)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glColor4f(0,0,0, 0.25f);
        glBegin(GL_QUADS);
        float sw = (float )w * (1.0/scale);
        glVertex2d(   10, 26);
        glVertex2d(sw-10, 26);
        glVertex2d(sw-10, 26 + 16 * 9);
        glVertex2d(   10, 26 + 16 * 9);
        glEnd();

        glDisable(GL_BLEND);
    }

    glColor3f(0,0,0);
    //font.Draw(x, 0, GameInput::textStr.c_str());

    glColor4f(0,0,0, fade_coeff);
    int y = 10;
    font.Draw(10, y, GameInput::textStr.c_str());

    std::vector<std::pair<std::string, Color> >::reverse_iterator str = g_listChatStrings.rbegin();
    int counter = 9;
    while (str != g_listChatStrings.rend() && counter--)
    {
        Color c = (*str).second;
        glColor4f(c.r,c.g,c.b, fade_coeff);

        font.Draw(10, y+=(int )(16.0f * scale) + 2, (*str).first.c_str());
        *str++;
    }

    glPopMatrix();
}

void Game::RenderPlayerScore(int x, int y, Player *player, const GLFont &font)
{
    assert(player);

    int w, h;
    glfwGetWindowSize(&w, &h);

    player->Apply();

    glBegin(GL_QUADS);
    glVertex2d(x,y);
    glVertex2d(x+64,y);
    glVertex2d(x+64,y+32);
    glVertex2d(x,y+32);
    glEnd();

    glColor3f(0,0,0);

    //player->ApplyInverse();
    font.Draw(x+3, y + 4, itoa(player->GetScore()).c_str());
    font.Draw(x+64+4, y, player->GetName().c_str());

    glColor3f(0,0,0);
    glLineWidth(2);

    if (player == g_Cube.Lock().GetCurrentPlayer())
        glColor3f(1,1,1);
    g_Cube.Unlock();

    glBegin(GL_LINE_STRIP);
    glVertex2d(x,y);
    glVertex2d(x+64,y);
    glVertex2d(x+64,y+32);
    glVertex2d(x,y+32);
    glVertex2d(x,y);
    glEnd();
}
